#include "torus.h"

#include "ray.h"
#include "solver.h"
#include "sphere.h"

#include <cmath>
#include <cstdlib>
#include <limits>

namespace raytrace {

Torus::Torus(const Vector3& pos, float inner_r, float outer_r,
             const Material* material)
    : Primitive(pos, material), inner_r_(inner_r), outer_r_(outer_r) {
  bounding_sphere_ = new Sphere(pos, inner_r_ + outer_r_, NULL);
}

Torus::~Torus() {
  delete bounding_sphere_;
}

bool Torus::IntersectsWith(const Ray& ray, float& d) const {
  if (!bounding_sphere_->IntersectsWith(ray, d))
    return false;

  const Vector3 o = Vector3::sub(ray.o_, pos_);

	const float alpha = Vector3::dot(ray.d_, ray.d_);
	const float beta = 2 * Vector3::dot(o, ray.d_);
	const float gamma = Vector3::dot(o, o) -
                      (inner_r_ * inner_r_) - (outer_r_ * outer_r_);

  std::vector<float> coeffs;
  coeffs.push_back(alpha * alpha);
  coeffs.push_back(2 * alpha * beta);
  coeffs.push_back((beta * beta) + (2 * alpha * gamma) +
                   (4 * outer_r_ * outer_r_ * ray.d_.y_ * ray.d_.y_));
  coeffs.push_back((2 * beta * gamma) +
                   (8 * outer_r_ * outer_r_ * o.y_ * ray.d_.y_));
  coeffs.push_back((gamma * gamma) + (4 * outer_r_ * outer_r_ * o.y_ * o.y_) -
                   (4 * outer_r_ * outer_r_ * inner_r_ * inner_r_));

  std::vector<float> solutions;
  solver::Solve(coeffs, solutions);
	
  if (solutions.empty())
    return false;

	d = std::numeric_limits<float>::max();
  for (std::vector<float>::iterator it = solutions.begin();
       it != solutions.end();
       ++it) {
	  d = std::min(d, *it);
  }
	
	return true;
}

Vector3 Torus::NormalAt(const Vector3& pt) const {
	const Vector3 p = Vector3::sub(pt, pos_);
	const float mult = p.length_sqr() -
                    (inner_r_ * inner_r_) - (outer_r_ * outer_r_);
	
	const Vector3 component = Vector3::scale(p, mult * 4);
	
	Vector3 n(component);
  n.y_ += 8 * outer_r_ * outer_r_ * p.y_;
	n.normalize();
	
	return n;
}

// TODO
Vector3 Torus::GetRandomPoint() const {
  return pos_;
}

}
